using System;

namespace Viper
{
	public class Disassembler
	{
		const byte PUSH = 0x01;
		const byte POP = 0x02;
		const byte SSS = 0x03;
		const byte LSS = 0x04;
		const byte SSP = 0x05;
		const byte LSP = 0x06;
		const byte LOAD = 0x07;
		const byte STORE = 0x08;
		const byte BEQ = 0x09;
		const byte BNE = 0x0A;
		const byte BGT = 0x0B;
		const byte BLT = 0x0C;
		const byte BZ = 0x0D;
		const byte BNZ = 0xF0;
		const byte BRA = 0x0F;
		const byte BSR = 0x10;
		const byte FADD = 0x11;
		const byte ADD = 0x12;
		const byte FSUB = 0x13;
		const byte SUB = 0x14;
		const byte FMUL = 0x15;
		const byte MUL = 0x16;
		const byte FDIV = 0x17;
		const byte DIV = 0x18;
		const byte SYSF = 0x27;
		const byte BRK = 0xFF;
		const byte MUL_S = 0x31;
		const byte DIV_S = 0x30;
		const byte TEQ = 0x39;
		const byte TNE = 0x40;
		const byte TGT = 0x44;
		const byte TLT = 0x46;
		const byte TGT_S = 0x45;
		const byte TLT_S = 0x47;
		const byte TGT_F = 0x48;
		const byte TLT_F = 0x49;
		const byte BEGIN_FAULT = 0x50;
		const byte END_FAULT = 0x51;
		const byte THROW = 0x52;
		const byte AND = 0x32;
		const byte BOR = 0x33;
		const byte XOR = 0x34;
		const byte RET = 0x28;
		const byte LDELEM_B = 0x5A;
		const byte LDELEM_W = 0x5B;
		const byte LDELEM_D = 0x5C;
		const byte LDELEM_Q = 0x5D;
		const byte STELEM_D = 0x60;
		const byte DUP = 0x59;
		const byte CONVD_B = 0x61;
		const byte CONVB_D = 0x54;
		const byte HEAPALLOC = 0x29;
		const byte HEAPFREE	= 0x62;
		const byte STACKALLOC = 0x05;
		public static Assembly Disassemble(Assembly rawAssembly)
		{
			Assembly ret = (Assembly)rawAssembly.Clone();
			ret.innerCode.Clear();
			foreach(Instruction il in rawAssembly.innerCode)
			{
				int operand = 0;
				if(il.Bytecode[1] == 4)
					operand = BitConverter.ToInt32(il.Bytecode, 2);
				switch(il.Bytecode[0])
				{
				case PUSH:
					if(il.Bytecode[1] == 4)
						ret.Emit(new Opcodes.push_d(BitConverter.ToUInt32(il.Bytecode,2)));
					else if(il.Bytecode[1] == 2)
						ret.Emit(new Opcodes.push_w(BitConverter.ToUInt16(il.Bytecode,2)));
					else if(il.Bytecode[1] == 1)
						ret.Emit(new Opcodes.push_b(il.Bytecode[2]));
					break;
				case STORE:
					if(il.Bytecode[1] == 4)
						ret.Emit(new Opcodes.dstore());
					else if(il.Bytecode[1] == 2)
						ret.Emit(new Opcodes.wstore());
					else if(il.Bytecode[1] == 1)
						ret.Emit(new Opcodes.bstore());
					break;
				case LOAD:
					if(il.Bytecode[1] == 4)
						ret.Emit(new Opcodes.dload());
					else if(il.Bytecode[1] == 2)
						ret.Emit(new Opcodes.wload());
					else if(il.Bytecode[1] == 1)
						ret.Emit(new Opcodes.bload());
					break;
				case POP:
					if(il.Bytecode[1] == 4)
						ret.Emit(new Opcodes.pop_d());
					else if(il.Bytecode[1] == 2)
						ret.Emit(new Opcodes.pop_w());
					else if(il.Bytecode[1] == 1)
						ret.Emit(new Opcodes.pop_w());
					break;
				case SYSF:
					ret.Emit(new Opcodes.sysf((uint)operand));
					break;
				case TEQ:
					ret.Emit(new Opcodes.teq());
					break;
				case TNE:
					ret.Emit(new Opcodes.tne());
					break;
				case TLT:
					ret.Emit(new Opcodes.tlt());
					break;
				case TGT:
					ret.Emit(new Opcodes.tgt());
					break;
				case ADD:
					ret.Emit(new Opcodes.Add());
					break;
				case 0x25:
					ret.Emit(new Opcodes.mod());
					break;
				case DIV:
					ret.Emit(new Opcodes.div());
					break;
				case DIV_S:
					ret.Emit(new Opcodes.div_s());
					break;
				case MUL:
					ret.Emit(new Opcodes.mul());
					break;
				case MUL_S:
					ret.Emit(new Opcodes.mul_s());
					break;
				case SUB:
					ret.Emit(new Opcodes.sub());
					break;
				case AND:
					ret.Emit(new Opcodes.and());
					break;
				case BOR:
					ret.Emit(new Opcodes.bor());
					break;
				case XOR:
					ret.Emit(new Opcodes.xor());
					break;
				case LDELEM_D:
					ret.Emit(new Opcodes.ldelem_d());
					break;
				case HEAPFREE:
					ret.Emit(new Opcodes.heapfree());
					break;
				case HEAPALLOC:
					ret.Emit(new Opcodes.heapalloc());
					break;
				case STACKALLOC:
					ret.Emit(new Opcodes.stack_alloc());
					break;
				case CONVB_D:
					ret.Emit(new Opcodes.convb_d());
					break;
				case CONVD_B:
					ret.Emit(new Opcodes.convd_b());
					break;
				case RET:
					ret.Emit(new Opcodes.ret());
					break;
				case BRA:
					if(rawAssembly.LookupSymbol((uint)(il.Address + operand)) != null)
					ret.Emit(new Opcodes.bra(rawAssembly.LookupSymbol((uint)(il.Address + operand))));
					break;
				case BSR:
					if(rawAssembly.LookupSymbol((uint)(il.Address + operand)) != null)
					ret.Emit(new Opcodes.bsr(rawAssembly.LookupSymbol((uint)(il.Address + operand))));
					break;
				case BZ:
					if(rawAssembly.LookupSymbol((uint)(il.Address + operand)) != null)
					ret.Emit(new Opcodes.bz(rawAssembly.LookupSymbol((uint)(il.Address + operand))));
					break;
				case BNZ:
					if(rawAssembly.LookupSymbol((uint)(il.Address + operand)) != null)
					ret.Emit(new Opcodes.bnz(rawAssembly.LookupSymbol((uint)(il.Address + operand))));
					break;
				case BLT:
					if(rawAssembly.LookupSymbol((uint)(il.Address + operand)) != null)
					ret.Emit(new Opcodes.blt(rawAssembly.LookupSymbol((uint)(il.Address + operand))));
					break;
				case BGT:
					if(rawAssembly.LookupSymbol((uint)(il.Address + operand)) != null)
					ret.Emit(new Opcodes.bgt(rawAssembly.LookupSymbol((uint)(il.Address + operand))));
					break;
				case BEQ:
					if(rawAssembly.LookupSymbol((uint)(il.Address + operand)) != null)
					ret.Emit(new Opcodes.beq(rawAssembly.LookupSymbol((uint)(il.Address + operand))));
					break;
				case BNE:
					if(rawAssembly.LookupSymbol((uint)(il.Address + operand)) != null)
					ret.Emit(new Opcodes.bne(rawAssembly.LookupSymbol((uint)(il.Address + operand))));
					break;
				default:
					il.Mnemonic = "Vil_Op_" + il.Bytecode[0].ToString("X2");
					ret.Emit(il);
					break;
				}
			}
			ret.position = 0;
			return ret;
		}
	}
}

